package jvDeformation;

import handler.DeformationPoint;
import handler.LineHandler;

import java.awt.Image;
import java.awt.Menu;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Vector;

import jv.geom.PgPolygonSet;
import jv.object.PsPanel;
import jv.object.PsUpdateIf;
import jv.project.PgGeometry;
import jv.project.PgGeometryIf;
import jv.project.PgJvxSrc;
import jv.project.PvDisplayIf;
import jv.project.PvGeometryIf;
import jv.project.PvPickEvent;
import jv.project.PvViewerIf;
import jv.rsrc.PsAuthorInfo;
import jv.rsrc.PsGeometryInfo;
import jv.vecmath.PdMatrix;
import jv.vecmath.PdVector;
import jv.vecmath.PiVector;

public class JvDeformationHandler implements PgGeometryIf{

	
	private final PgPolygonSet polySet;
	private boolean sticked;
	/**
	 * @return the sticked
	 */
	public boolean isSticked() {
		return sticked;
	}

	private LineHandler lineHandler= null;
	private final Vector<PvDisplayIf> displays = new Vector<PvDisplayIf>();
	public JvDeformationHandler() {
		polySet = new PgPolygonSet(2);
		polySet.setDimOfPolygons(2);
		polySet.showVertices(true);
		polySet.setGlobalVertexSize(4.0);
		polySet.showVertexLabels(true);
		polySet.setName(this.getClass().getSimpleName());
		sticked = false;
	}
	
	/**
	 * @param center
	 * @see jv.project.PgGeometry#setCenter(jv.vecmath.PdVector)
	 */
	public void setCenter(PdVector center) {
		polySet.setCenter(center);
	}
	
	private ArrayList<JvDeformationHandlerListener> jvDeformationHandlerListeners  = new ArrayList<>();
	
	public boolean addJvDeformationHandlerListener(JvDeformationHandlerListener jvDeformationHandlerListener){
		
		boolean add = jvDeformationHandlerListeners.add(jvDeformationHandlerListener);
		if (add && sticked) {
			jvDeformationHandlerListener.stickPoints(getLineHandler());
		}
		return add;
	}
	
	public boolean removeJvDeformationHandlerListener(JvDeformationHandlerListener jvDeformationHandlerListener){
		return jvDeformationHandlerListeners.remove(jvDeformationHandlerListener);
	}

	private ArrayList<JvDeformationHandler> syncHandlers = new ArrayList<>();
	public void sync(JvDeformationHandler syncHandler){
		syncHandlers.add(syncHandler);
	}
	
	public boolean removeSync(JvDeformationHandler syncHandler){
		return syncHandlers.remove(syncHandler);
	}
	
	public LineHandler getLineHandler(){
		return lineHandler;
	}

	public LineHandler stickPointsOnImage(){
		for (JvDeformationHandler defo : syncHandlers) {
			defo.stickPointsOnImage_private();
			defo.update(defo);
		}
		return this.stickPointsOnImage_private();
	}
	
	private synchronized LineHandler stickPointsOnImage_private(){
		sticked = true;
		DeformationPoint[] basePoints = new DeformationPoint[polySet.getNumVertices()];
		for (int i = 0; i < basePoints.length; i++) {
			basePoints[i]= new DeformationPoint(polySet.getVertex(i).m_data);
		}
		int[][] lines = new int[polySet.getNumPolygons()][2];
		for (int i = 0; i < lines.length; i++) {
			lines[i] = polySet.getPolygon(i).m_data;
		}
		this.lineHandler = new LineHandler(basePoints, lines);
		for (JvDeformationHandlerListener listener : jvDeformationHandlerListeners) {
			listener.stickPoints(this.lineHandler);
		}
		return this.lineHandler;
	}
	
	
	public LineHandler generateHandler(){
		DeformationPoint[] basePoints = new DeformationPoint[polySet.getNumVertices()];
		for (int i = 0; i < basePoints.length; i++) {
			basePoints[i]= new DeformationPoint(polySet.getVertex(i).m_data);
		}
		int[][] lines = new int[polySet.getNumPolygons()][2];
		for (int i = 0; i < lines.length; i++) {
			lines[i] = polySet.getPolygon(i).m_data;
		}
		return new LineHandler(basePoints, lines);
	}
	

	
	public void releasePoints(){
		if (sticked == false) {
			return;
		}
		sticked = false;
		//lineHandler.getMovedPoints();
		PdVector[] vertices = new PdVector[lineHandler.getBasePoints().size()];
		int i=0;
		for (DeformationPoint p : lineHandler.getBasePoints()) {
			vertices[i++] = new PdVector(p.toArray());
		}
		polySet.setVertices(vertices);
		lineHandler= null;
		for (JvDeformationHandler defo : syncHandlers) {
			defo.releasePoints();
		}
		for (JvDeformationHandlerListener listener : jvDeformationHandlerListeners) {
			listener.unstick();
		}
		repaint();
	}
	
	
	
	
	public void repaint(){
		for (PvDisplayIf d : displays) {
			d.update(this);
		}
	}
	
	/**
	 * @param display
	 * @return
	 * @see jv.project.PgGeometryIf#addDisplay(jv.project.PvDisplayIf)
	 */
	public boolean addDisplay(PvDisplayIf display) {
		return displays.add(display);
	}

	/**
	 * @param anElement
	 * @return
	 * @see jv.project.PgGeometryIf#addElement(jv.vecmath.PiVector)
	 */
	public int addElement(PiVector anElement) {
		if (sticked) {
			return -1;
		}
		return polySet.addElement(anElement);
	}

	/**
	 * @param aPolygon
	 * @return
	 * @see jv.project.PgGeometryIf#addPolygon(jv.vecmath.PiVector)
	 */
	public int addPolygon(PiVector aPolygon) {
		if (sticked) {
			return -1;
		}
		for (JvDeformationHandler defo : syncHandlers) {
			defo.addPolygon_private(PiVector.copyNew(aPolygon));
			defo.update(defo);
		}
		return addPolygon_private(aPolygon);//;elementSet.addPolygon(aPolygon);
	}
	
	
	
	public int addPolygon_private(PiVector aPolygon) {
		if (sticked) {
			return -1;
		}
		
		int back=-1;
		for (int i = 0; i < aPolygon.getSize()-1; i++) {
			PiVector pi = new PiVector(aPolygon.getEntry(i), aPolygon.getEntry(i+1));
			back =polySet.addPolygon(pi);
		}
		return back;//;elementSet.addPolygon(aPolygon);
	}
	

	/**
	 * @param aVertex
	 * @return
	 * @see jv.project.PgGeometryIf#addVertex(jv.vecmath.PdVector)
	 */
	public int addVertex(PdVector aVertex) {
		if (sticked) {
			return -1;
		}
		for (JvDeformationHandler defo : syncHandlers) {
			defo.addVertex_private(PdVector.copyNew(aVertex));
			defo.update(defo);
		}
		return polySet.addVertex(aVertex);
	}
	
	
	private void addVertex_private(PdVector aVertex){
		if (sticked) {
			return;
		}
		polySet.addVertex(aVertex);
	}

	/**
	 * @param type
	 * @param ext
	 * @return
	 * @see jv.project.PgGeometryIf#assureInspector(java.lang.String, java.lang.String)
	 */
	public PsPanel assureInspector(String type, String ext) {
		return polySet.assureInspector(type, ext);
	}

	/**
	 * @param s
	 * @param a
	 * @param t
	 * @param b
	 * @return
	 * @see jv.project.PgGeometryIf#blend(double, jv.project.PgGeometry, double, jv.project.PgGeometry)
	 */
	public boolean blend(double s, PgGeometry a, double t, PgGeometry b) {
		return polySet.blend(s, a, t, b);
	}

	/**
	 * @param index
	 * @param aTag
	 * @see jv.project.PgGeometryIf#clearTagElement(int, int)
	 */
	public void clearTagElement(int index, int aTag) {
		polySet.clearTagElement(index, aTag);
	}

	/**
	 * @param index
	 * @param aTag
	 * @see jv.project.PgGeometryIf#clearTagPolygon(int, int)
	 */
	public void clearTagPolygon(int index, int aTag) {
		polySet.clearTagPolygon(index, aTag);
	}

	/**
	 * @param index
	 * @param aTag
	 * @see jv.project.PgGeometryIf#clearTagVertex(int, int)
	 */
	public void clearTagVertex(int index, int aTag) {
		for (JvDeformationHandler defo : syncHandlers) {
			defo.polySet.clearTagVertex(index, aTag);
			defo.update(defo);
		}
		polySet.clearTagVertex(index, aTag);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#clone()
	 */
	public Object clone() {
		return polySet.clone();
	}

	/**
	 * @param menu
	 * @param viewer
	 * @return
	 * @see jv.project.PgGeometryIf#fillMethodMenu(java.awt.Menu, jv.project.PvViewerIf)
	 */
	public Menu fillMethodMenu(Menu menu, PvViewerIf viewer) {
		return polySet.fillMethodMenu(menu, viewer);
	}

	/**
	 * @param menu
	 * @return
	 * @deprecated
	 * @see jv.project.PgGeometryIf#fillMethodMenu(java.awt.Menu)
	 */
	public Menu fillMethodMenu(Menu menu) {
		return polySet.fillMethodMenu(menu);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getAmbientInvMatrix()
	 */
	public PdMatrix getAmbientInvMatrix() {
		return polySet.getAmbientInvMatrix();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getAmbientMatrix()
	 */
	public PdMatrix getAmbientMatrix() {
		return polySet.getAmbientMatrix();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getAmbientProjection()
	 */
	public int getAmbientProjection() {
		return polySet.getAmbientProjection();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getAmbientSpace()
	 */
	public int getAmbientSpace() {
		return polySet.getAmbientSpace();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getAuthorInfo()
	 */
	public PsAuthorInfo getAuthorInfo() {
		return polySet.getAuthorInfo();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getControlPanel()
	 */
	public PsPanel getControlPanel() {
		return polySet.getControlPanel();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getDimOfSimplex()
	 */
	public int getDimOfSimplex() {
		return polySet.getDimOfSimplex();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getDimOfVertices()
	 */
	public int getDimOfVertices() {
		return polySet.getDimOfVertices();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getDisplays()
	 */
	public Enumeration<PvDisplayIf> getDisplays() {
		return  displays.elements();
	}

	/**
	 * @param anIndex
	 * @return
	 * @see jv.project.PgGeometryIf#getElement(int)
	 */
	public PiVector getElement(int anIndex) {
		return polySet.getElement(anIndex);
	}

	/**
	 * @return
	 * @see jv.object.PsUpdateIf#getFather()
	 */
	public PsUpdateIf getFather() {
		return polySet.getFather();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getGeometryInfo()
	 */
	public PsGeometryInfo getGeometryInfo() {
		return polySet.getGeometryInfo();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getInfoPanel()
	 */
	public PsPanel getInfoPanel() {
		return polySet.getInfoPanel();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getJvx()
	 */
	public PgJvxSrc getJvx() {
		return polySet.getJvx();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getMaterialPanel()
	 */
	public PsPanel getMaterialPanel() {
		return polySet.getMaterialPanel();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getModelMatrix()
	 */
	public PdMatrix getModelMatrix() {
		return polySet.getModelMatrix();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getName()
	 */
	public String getName() {
		return polySet.getName();
	}

	/**
	 * @param anIndex
	 * @return
	 * @see jv.project.PgGeometryIf#getPolygon(int)
	 */
	public PiVector getPolygon(int anIndex) {
		return polySet.getPolygon(anIndex);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getTextureImage()
	 */
	public Image getTextureImage() {
		return polySet.getTextureImage();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getTextureImageName()
	 */
	public String getTextureImageName() {
		return polySet.getTextureImageName();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getTitle()
	 */
	public String getTitle() {
		return polySet.getTitle();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getType()
	 */
	public int getType() {
		return polySet.getType();
	}

	/**
	 * @param anIndex
	 * @return
	 * @see jv.project.PgGeometryIf#getVertex(int)
	 */
	public PdVector getVertex(int anIndex) {
		return polySet.getVertex(anIndex);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#hasAmbientMatrix()
	 */
	public boolean hasAmbientMatrix() {
		return polySet.hasAmbientMatrix();
	}

	/**
	 * @param display
	 * @return
	 * @see jv.project.PgGeometryIf#hasDisplay(jv.project.PvDisplayIf)
	 */
	public boolean hasDisplay(PvDisplayIf display) {
		return polySet.hasDisplay(display);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#hasModelMatrix()
	 */
	public boolean hasModelMatrix() {
		return polySet.hasModelMatrix();
	}

	/**
	 * @param aBit
	 * @return
	 * @see jv.project.PgGeometryIf#hasTag(int)
	 */
	public boolean hasTag(int aBit) {
		return polySet.hasTag(aBit);
	}

	/**
	 * @param index
	 * @param aTag
	 * @return
	 * @see jv.project.PgGeometryIf#hasTagElement(int, int)
	 */
	public boolean hasTagElement(int index, int aTag) {
		return polySet.hasTagElement(index, aTag);
	}

	/**
	 * @param index
	 * @param aTag
	 * @return
	 * @see jv.project.PgGeometryIf#hasTagPolygon(int, int)
	 */
	public boolean hasTagPolygon(int index, int aTag) {
		return polySet.hasTagPolygon(index, aTag);
	}

	/**
	 * @param index
	 * @param aTag
	 * @return
	 * @see jv.project.PgGeometryIf#hasTagVertex(int, int)
	 */
	public boolean hasTagVertex(int index, int aTag) {
		return polySet.hasTagVertex(index, aTag);
	}

	/**
	 * @param base
	 * @param dir
	 * @return
	 * @see jv.project.PgGeometryIf#intersectionWithLine(jv.vecmath.PdVector, jv.vecmath.PdVector)
	 */
	public PvPickEvent intersectionWithLine(PdVector base, PdVector dir) {
		return polySet.intersectionWithLine(base, dir);
	}

	/**
	 * @param panel
	 * @return
	 * @see jv.project.PgGeometryIf#isConfigurable(int)
	 */
	public boolean isConfigurable(int panel) {
		return polySet.isConfigurable(panel);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#isVisible()
	 */
	public boolean isVisible() {
		return polySet.isVisible();
	}

	/**
	 * @param geom
	 * @return
	 * @see jv.project.PgGeometryIf#merge(jv.project.PgGeometryIf)
	 */
	public boolean merge(PgGeometryIf geom) {
		return polySet.merge(geom);
	}

	/**
	 * @param dc
	 * @see jv.project.PgGeometryIf#paint(jv.project.PvGeometryIf)
	 */
	public void paint(PvGeometryIf dc) {
		polySet.paint(dc);
		dc.setDrawingOrder(PvGeometryIf.DRAW_ORDER_FRONT);
	}

	/**
	 * @param mat
	 * @param bOrient
	 * @param bVector
	 * @return
	 * @see jv.project.PgGeometryIf#reflect(jv.vecmath.PdMatrix, boolean, boolean)
	 */
	public PgGeometry reflect(PdMatrix mat, boolean bOrient, boolean bVector) {
		return polySet.reflect(mat, bOrient, bVector);
	}

	/**
	 * @param display
	 * @return
	 * @see jv.project.PgGeometryIf#removeDisplay(jv.project.PvDisplayIf)
	 */
	public boolean removeDisplay(PvDisplayIf display) {
		return displays.remove(display);
	}

	/**
	 * @param anIndex
	 * @return
	 * @see jv.project.PgGeometryIf#removeElement(int)
	 */
	public int[] removeElement(int anIndex) {
		if (sticked) {
			return null;
		}
		for (JvDeformationHandler defo : syncHandlers) {
			defo.removeElement_private(anIndex);
			defo.update(defo);
		}
		int[] removeElement_private = removeElement_private(anIndex);
		repaint();
		return removeElement_private;
	}
	
	
	private int[] removeElement_private(int anIndex) {
		if (sticked) {
			return null;
		}
		return polySet.removeElement(anIndex);
	}
	
	
	

	/**
	 * 
	 * @see jv.project.PgGeometryIf#removeMethodMenus()
	 */
	public void removeMethodMenus() {
		polySet.removeMethodMenus();
	}

	/**
	 * @param anIndex
	 * @return
	 * @see jv.project.PgGeometryIf#removePolygon(int)
	 */
	public int[] removePolygon(int anIndex) {
		if (sticked) {
			return null;
		}
		for (JvDeformationHandler defo : syncHandlers) {
			defo.removePolygon_private(anIndex);
			defo.update(defo);
		}
		int[] removePolygon_private = removePolygon_private(anIndex);
		repaint();
		return removePolygon_private;
	}
	
	
	private int[] removePolygon_private(int anIndex) {
		return polySet.removePolygon(anIndex);
	}

	/**
	 * @param anIndex
	 * @return
	 * @see jv.project.PgGeometryIf#removeVertex(int)
	 */
	public int[] removeVertex(int anIndex) {
		if (sticked) {
			return null;
		}
		for (JvDeformationHandler defo : syncHandlers) {
			defo.removeVertex_private(anIndex);
			defo.update(defo);
		}
		int[] removeVertex_private = removeVertex_private(anIndex);
		repaint();
		return removeVertex_private;
	}
	
	private int[] removeVertex_private(int anIndex) {
		if (sticked) {
			return null;
		}
		return polySet.removeVertex(anIndex);
	}

	/**
	 * @param ambientMat
	 * @param ambientInvMat
	 * @see jv.project.PgGeometryIf#setAmbientMatrix(jv.vecmath.PdMatrix, jv.vecmath.PdMatrix)
	 */
	public void setAmbientMatrix(PdMatrix ambientMat, PdMatrix ambientInvMat) {
		polySet.setAmbientMatrix(ambientMat, ambientInvMat);
	}

	/**
	 * @param mode
	 * @see jv.project.PgGeometryIf#setAmbientProjection(int)
	 */
	public void setAmbientProjection(int mode) {
		polySet.setAmbientProjection(mode);
	}

	/**
	 * @param mode
	 * @see jv.project.PgGeometryIf#setAmbientSpace(int)
	 */
	public void setAmbientSpace(int mode) {
		polySet.setAmbientSpace(mode);
	}

	/**
	 * @param authorInfo
	 * @see jv.project.PgGeometryIf#setAuthorInfo(jv.rsrc.PsAuthorInfo)
	 */
	public void setAuthorInfo(PsAuthorInfo authorInfo) {
		polySet.setAuthorInfo(authorInfo);
	}

	/**
	 * @param anIndex
	 * @param anElement
	 * @return
	 * @see jv.project.PgGeometryIf#setElement(int, jv.vecmath.PiVector)
	 */
	public boolean setElement(int anIndex, PiVector anElement) {
		if (sticked) {
			return false;
		}
		return polySet.setElement(anIndex, anElement);
	}

	/**
	 * @param geometryInfo
	 * @see jv.project.PgGeometryIf#setGeometryInfo(jv.rsrc.PsGeometryInfo)
	 */
	public void setGeometryInfo(PsGeometryInfo geometryInfo) {
		polySet.setGeometryInfo(geometryInfo);
	}

	/**
	 * @param src
	 * @see jv.project.PgGeometryIf#setJvx(jv.project.PgJvxSrc)
	 */
	public void setJvx(PgJvxSrc src) {
		polySet.setJvx(src);
	}

	/**
	 * @param modelMat
	 * @see jv.project.PgGeometryIf#setModelMatrix(jv.vecmath.PdMatrix)
	 */
	public void setModelMatrix(PdMatrix modelMat) {
		polySet.setModelMatrix(modelMat);
	}

	/**
	 * @param name
	 * @see jv.project.PgGeometryIf#setName(java.lang.String)
	 */
	public void setName(String name) {
		polySet.setName(name);
	}

	/**
	 * @param parent
	 * @see jv.object.PsUpdateIf#setParent(jv.object.PsUpdateIf)
	 */
	public void setParent(PsUpdateIf parent) {
		polySet.setParent(parent);
	}

	/**
	 * @param anIndex
	 * @param aPolygon
	 * @return
	 * @see jv.project.PgGeometryIf#setPolygon(int, jv.vecmath.PiVector)
	 */
	public boolean setPolygon(int anIndex, PiVector aPolygon) {
		return polySet.setPolygon(anIndex, aPolygon);
	}

	/**
	 * @param key
	 * @param state
	 * @see jv.project.PgGeometryIf#setState(int, boolean)
	 */
	public void setState(int key, boolean state) {
		polySet.setState(key, state);
	}

	/**
	 * @param index
	 * @param aTag
	 * @see jv.project.PgGeometryIf#setTagElement(int, int)
	 */
	public void setTagElement(int index, int aTag) {
		polySet.setTagElement(index, aTag);
	}

	/**
	 * @param index
	 * @param aTag
	 * @see jv.project.PgGeometryIf#setTagPolygon(int, int)
	 */
	public void setTagPolygon(int index, int aTag) {
		polySet.setTagPolygon(index, aTag);
	}

	/**
	 * @param index
	 * @param aTag
	 * @see jv.project.PgGeometryIf#setTagVertex(int, int)
	 */
	public void setTagVertex(int index, int aTag) {
		
		for (JvDeformationHandler defo : syncHandlers) {
			defo.polySet.setTagVertex(index, aTag);
			defo.update(defo);
		}
		polySet.setTagVertex(index, aTag);
	}

	/**
	 * @param img
	 * @see jv.project.PgGeometryIf#setTextureImage(java.awt.Image)
	 */
	public void setTextureImage(Image img) {
		polySet.setTextureImage(img);
	}

	/**
	 * @param name
	 * @see jv.project.PgGeometryIf#setTextureImageName(java.lang.String)
	 */
	public void setTextureImageName(String name) {
		polySet.setTextureImageName(name);
	}

	/**
	 * @param title
	 * @see jv.project.PgGeometryIf#setTitle(java.lang.String)
	 */
	public void setTitle(String title) {
		polySet.setTitle(title);
	}

	/**
	 * @param anIndex
	 * @param aVertex
	 * @return
	 * @see jv.project.PgGeometryIf#setVertex(int, jv.vecmath.PdVector)
	 */
	public boolean setVertex(int anIndex, PdVector aVertex) {
		if (sticked) {
			this.lineHandler.move(anIndex, new DeformationPoint(aVertex.m_data));
			repaint();
			return true;
		}
		return polySet.setVertex(anIndex, aVertex);
	}

	/**
	 * @param flag
	 * @see jv.project.PgGeometryIf#setVisible(boolean)
	 */
	public void setVisible(boolean flag) {
		polySet.setVisible(flag);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#toString()
	 */
	public String toString() {
		return polySet.toString();
	}

	/**
	 * @param event
	 * @return
	 * @see jv.object.PsUpdateIf#update(java.lang.Object)
	 */
	public boolean update(Object event) {
		if (this == event) {
			repaint();
			return true;
		}
		return false;
	}

}
